Passed
Push — master ( 8e61b0...7390f2 )
by Jan
05:14
created

jquery.tristate.js ➔ Plugin   A

Complexity

Conditions 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
dl 0
loc 7
rs 10
1
/*jslint devel: true, bitwise: true, regexp: true, browser: true, confusion: true, unparam: true, eqeq: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
2
/*globals jQuery */
3
4
/*!
5
 * Tristate v1.2.1
6
 *
7
 * Copyright (c) 2013-2017 Martijn W. van der Lee
8
 * Licensed under the MIT.
9
 */
10
/* Based on work by:
11
 *  Chris Coyier (http://css-tricks.com/indeterminate-checkboxes/)
12
 *
13
 * Tristate checkbox with support features
14
 * pseudo selectors
15
 * val() overwrite
16
 */
17
18
;(function($, undefined) {   
19
	'use strict';
20
	
21
	var pluginName = 'tristate',
22
		defaults = {
23
			'change':			undefined,
24
			'checked':			undefined,
25
			'indeterminate':	undefined,
26
			'init':				undefined,
27
			'reverse':			false,
28
			'state':			undefined,
29
			'unchecked':		undefined,
30
			'value':			undefined	// one-way only!
31
		},
32
		valFunction	= $.fn.val;
33
34
    function Plugin(element, options) {
35
        if($(element).is(':checkbox')) {        
36
            this.element = $(element);
37
            this.settings = $.extend( {}, defaults, options );
38
            this._create();
39
        }
40
    }
41
42
    $.extend(Plugin.prototype, {
43
		_create: function() {					
44
			var that = this,
45
				state;
46
47
			// Fix for #1
48
			if (window.navigator.userAgent.indexOf('Trident') >= 0) {
49
				this.element.click(function(e) {
50
					that._change.call(that, e);			
51
					that.element.closest('form').change();
52
				});
53
			} else {
54
				this.element.change(function(e) {
55
					that._change.call(that, e);
56
				});
57
			}
58
59
			this.settings.checked		= this.element.attr('checkedvalue')		  || this.settings.checked;
60
			this.settings.unchecked		= this.element.attr('uncheckedvalue')	  || this.settings.unchecked;
61
			this.settings.indeterminate	= this.element.attr('indeterminatevalue') || this.settings.indeterminate;
62
63
			// Initially, set state based on option state or attributes
64
			if (typeof this.settings.state === 'undefined') {
65
				this.settings.state		= typeof this.element.attr('indeterminate') !== 'undefined'? null : this.element.is(':checked');
66
			}
67
68
			// If value specified, overwrite with value
69
			if (typeof this.settings.value !== 'undefined') {
70
				state = this._parseValue(this.settings.value);
71
				if (typeof state !== 'undefined') {
72
					this.settings.state = state;
73
				}
74
			}
75
76
			this._refresh(this.settings.init);
77
78
			return this;
79
		},
80
81
		_change: function(e) {
82
			if (e.isTrigger || !e.hasOwnProperty('which')) {
83
				e.preventDefault();
84
			}
85
			
86
			switch (this.settings.state) {
87
				case true:  this.settings.state = (this.settings.reverse ? false : null); break;
88
				case false: this.settings.state = (this.settings.reverse ? null : true); break;
89
				default:    this.settings.state = (this.settings.reverse ? true : false); break;
90
			}
91
92
			this._refresh(this.settings.change);								
93
		},
94
		
95
		_refresh: function(callback) {
96
			var value	= this.value();
97
98
			this.element.data("vanderlee." + pluginName, value);
99
100
			this.element[this.settings.state === null ? 'attr' : 'removeAttr']('indeterminate', 'indeterminate');
101
			this.element.prop('indeterminate', this.settings.state === null);
102
			this.element.get(0).indeterminate = this.settings.state === null;
103
104
			this.element[this.settings.state === true ? 'attr' : 'removeAttr']('checked', true);
105
			this.element.prop('checked', this.settings.state === true);
106
107
			if ($.isFunction(callback)) {
108
				callback.call(this.element, this.settings.state, this.value());
109
			}
110
		},
111
112
		state: function(value) {
113
			if (typeof value === 'undefined') {
114
				return this.settings.state;
115
			} else if (value === true || value === false || value === null) {
116
				this.settings.state = value;
117
118
				this._refresh(this.settings.change);
119
			}
120
			return this;
121
		},
122
123
		_parseValue: function(value) {
124
			if (value === this.settings.checked) {
125
				return true;
126
			} else if (value === this.settings.unchecked) {
127
				return false;
128
			} else if (value === this.settings.indeterminate) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if value === this.settings.indeterminate is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
129
				return null;
130
			}
131
		},
132
133
		value: function(value) {
134
			if (typeof value === 'undefined') {
135
				var value;
136
				switch (this.settings.state) {
137
					case true:
138
						value = this.settings.checked;
139
						break;
140
141
					case false:
142
						value = this.settings.unchecked;
143
						break;
144
145
					case null:
146
						value = this.settings.indeterminate;
147
						break;
148
				}
149
				return typeof value === 'undefined'? this.element.attr('value') : value;
150
			} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
151
				var state = this._parseValue(value);
152
				if (typeof state !== 'undefined') {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if typeof state !== "undefined" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
153
					this.settings.state = state;
154
					this._refresh(this.settings.change);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
155
				}
156
			}
157
		}		
158
	});
159
160
	$.fn[pluginName] = function (options, value) {	
161
		var result = this;
162
		
163
		this.each(function() {
164
            if (!$.data(this, "plugin.vanderlee." + pluginName)) {
165
                $.data(this, "plugin.vanderlee." + pluginName, new Plugin(this, options));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
166
            } else if (typeof options === 'string') {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if typeof options === "string" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
167
				if (typeof value === 'undefined') {
168
					result = $(this).data("plugin.vanderlee." + pluginName)[options]();
169
					return false;
170
				} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
171
					$(this).data("plugin.vanderlee." + pluginName)[options](value);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
172
				}
173
			}
174
        });
175
176
		return result;
177
	};
178
	
179
	// Overwrite fn.val
180
    $.fn.val = function(value) {
181
        var data = this.data("vanderlee." + pluginName);
182
        if (typeof data === 'undefined') {
183
	        if (typeof value === 'undefined') {
184
	            return valFunction.call(this);
185
			} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
186
				return valFunction.call(this, value);
187
			}
188
		} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
189
	        if (typeof value === 'undefined') {
190
				return data;
191
			} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
192
				this.data("vanderlee." + pluginName, value);
193
				return this;
194
			}
195
		}
196
    };
197
198
	// :indeterminate pseudo selector
199
    $.expr.filters.indeterminate = function(element) {
200
		var $element = $(element);
201
		return typeof $element.data("vanderlee." + pluginName) !== 'undefined' && $element.prop('indeterminate');
202
    };
203
204
	// :determinate pseudo selector
205
    $.expr.filters.determinate = function(element) {
206
		return !($.expr.filters.indeterminate(element));
207
    };
208
209
	// :tristate selector
210
    $.expr.filters.tristate = function(element) {
211
		return typeof $(element).data("vanderlee." + pluginName) !== 'undefined';
212
    };
213
})(jQuery);
214